package logger

import (
	"github.com/gin-gonic/gin"
	"github.com/goccy/go-json"
	"github.com/google/uuid"
	"path/filepath"
	"telemple_web/app/core/config"
)

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"time"
)

const logTimeTpl = "2006-01-02T15:04:05.000000:00"

type consoleColorModeValue int

const (
	autoColor consoleColorModeValue = iota
	disableColor
	forceColor
)

const (
	green   = "\033[97;42m"
	white   = "\033[90;47m"
	yellow  = "\033[90;43m"
	red     = "\033[97;41m"
	blue    = "\033[97;44m"
	magenta = "\033[97;45m"
	cyan    = "\033[97;46m"
	reset   = "\033[0m"
)

var consoleColorMode = autoColor

// LoggerConfig defines the config for Logger middleware.
type LoggerConfig struct {
	// Optional. Default value is gin.defaultLogFormatter
	Formatter LogFormatter

	// Output is a writer where logs are written.
	// Optional. Default value is gin.DefaultWriter.
	Output io.Writer

	// SkipPaths is an url path array which logs are not written.
	// Optional.
	SkipPaths []string

	// Skip is a Skipper that indicates which logs should not be written.
	// Optional.
	Skip Skipper
}

// Skipper is a function to skip logs based on provided Context
type Skipper func(c *gin.Context) bool

// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
type LogFormatter func(params LogFormatterParams) string

// LogFormatterParams is the structure any formatter will be handed when time to log comes
type LogFormatterParams struct {
	Request *http.Request

	// TimeStamp shows the time after the server returns a response.
	TimeStamp time.Time
	// StatusCode is HTTP response code.
	StatusCode int
	// Latency is how much time the server cost to process a certain request.
	Latency time.Duration
	// ClientIP equals Context's ClientIP method.
	ClientIP string
	// Method is the HTTP method given to the request.
	Method string
	// Path is a path the client requests.
	Path string
	// ErrorMessage is set if error has occurred in processing the request.
	ErrorMessage string
	// isTerm shows whether gin's output descriptor refers to a terminal.
	isTerm bool
	// BodySize is the size of the Response Body
	BodySize int
	// Keys are the keys set on the request's context.
	Keys map[string]any
}

// StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
func (p *LogFormatterParams) StatusCodeColor() string {
	code := p.StatusCode

	switch {
	case code >= http.StatusContinue && code < http.StatusOK:
		return white
	case code >= http.StatusOK && code < http.StatusMultipleChoices:
		return green
	case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
		return white
	case code >= http.StatusBadRequest && code < http.StatusInternalServerError:
		return yellow
	default:
		return red
	}
}

// MethodColor is the ANSI color for appropriately logging http method to a terminal.
func (p *LogFormatterParams) MethodColor() string {
	method := p.Method

	switch method {
	case http.MethodGet:
		return blue
	case http.MethodPost:
		return cyan
	case http.MethodPut:
		return yellow
	case http.MethodDelete:
		return red
	case http.MethodPatch:
		return green
	case http.MethodHead:
		return magenta
	case http.MethodOptions:
		return white
	default:
		return reset
	}
}

// ResetColor resets all escape attributes.
func (p *LogFormatterParams) ResetColor() string {
	return reset
}

// IsOutputColor indicates whether can colors be outputted to the log.
func (p *LogFormatterParams) IsOutputColor() bool {
	return consoleColorMode == forceColor || (consoleColorMode == autoColor && p.isTerm)
}

// defaultLogFormatter is the default log format function Logger middleware uses.
var defaultLogFormatter = func(param LogFormatterParams) (string, error) {

	var res = map[string]any{
		"ip":      param.ClientIP,
		"time":    param.TimeStamp.Format(logTimeTpl),
		"latency": param.Latency.Milliseconds(),
		"method":  param.Method,
		"path":    param.Path,
		"query":   param.Request.URL.RawQuery,
		"status":  param.StatusCode,
		"error":   param.ErrorMessage,
		"size":    param.BodySize,
		"header":  param.Request.Header,
		"referer": param.Request.Referer(),
		"body":    param.Request.Body,
	}

	resJson, err := json.Marshal(res)
	return string(resJson), err

	//var statusColor, methodColor, resetColor string
	//if param.IsOutputColor() {
	//	statusColor = param.StatusCodeColor()
	//	methodColor = param.MethodColor()
	//	resetColor = param.ResetColor()
	//}
	//
	//if param.Latency > time.Minute {
	//	param.Latency = param.Latency.Truncate(time.Second)
	//}
	//return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s",
	//	param.TimeStamp.Format("2006/01/02 - 15:04:05"),
	//	statusColor, param.StatusCode, resetColor,
	//	param.Latency,
	//	param.ClientIP,
	//	methodColor, param.Method, resetColor,
	//	param.Path,
	//	param.ErrorMessage,
	//)
}

// DisableConsoleColor disables color output in the console.
func DisableConsoleColor() {
	consoleColorMode = disableColor
}

// ForceConsoleColor force color output in the console.
func ForceConsoleColor() {
	consoleColorMode = forceColor
}

// ErrorLogger returns a HandlerFunc for any error type.
func ErrorLogger() gin.HandlerFunc {
	return ErrorLoggerT(gin.ErrorTypeAny)
}

// ErrorLoggerT returns a HandlerFunc for a given error type.
func ErrorLoggerT(typ gin.ErrorType) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()
		errors := c.Errors.ByType(typ)
		if len(errors) > 0 {
			c.JSON(-1, errors)
		}
	}
}

// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter.
// By default, gin.DefaultWriter = os.Stdout.
//
//	func Logger() gin.HandlerFunc {
//		return LoggerWithConfig(LoggerConfig{})
//	}
//
// // LoggerWithFormatter instance a Logger middleware with the specified log format function.
//
//	func LoggerWithFormatter(f LogFormatter) gin.HandlerFunc {
//		return LoggerWithConfig(LoggerConfig{
//			Formatter: f,
//		})
//	}
//
// // LoggerWithWriter instance a Logger middleware with the specified writer buffer.
// // Example: os.Stdout, a file opened in write mode, a socket...
//
//	func LoggerWithWriter(out io.Writer, notlogged ...string) gin.HandlerFunc {
//		return LoggerWithConfig(LoggerConfig{
//			Output:    out,
//			SkipPaths: notlogged,
//		})
//	}
func New(config config.WebServerLog) gin.HandlerFunc {

	return func(c *gin.Context) {
		// 日志输出类型
		start := time.Now()
		//out := io.Writer(os.Stdout)
		output, err := loadLoggerFile(config, start)
		if err != nil {
			panic(err)
		}
		c.Request.Header.Add("traceid", uuid.New().String())
		c.Next()
		formatter := defaultLogFormatter
		param := LogFormatterParams{
			Request: c.Request,
			Keys:    c.Keys,
		}
		str, err := formatter(param)
		fmt.Fprint(output, str)
	}
}

func loadLoggerFile(config config.WebServerLog, time time.Time) (io.Writer, error) {
	logPath := fmt.Sprintf(config.OutPath, time.Format("2006-01-02"))
	if !filepath.IsAbs(logPath) {
		exePath, err := os.Executable()
		if err == nil {
			logPath = filepath.Join(filepath.Dir(logPath), exePath)
		}
	}
	fmt.Println("logPath:", logPath)
	// 创建文件
	// 0666: 所有者、组、其他用户都有读写权限。
	// 0644: 所有者有读写权限，组和其他用户只有读权限。
	file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	return file, err

	// 监测文件是否存在
	//_, err := os.Stat(logPath)
	//if os.IsNotExist(err) {
	//	// 创建文件
	//	// 0666: 所有者、组、其他用户都有读写权限。
	//	// 0644: 所有者有读写权限，组和其他用户只有读权限。
	//	file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	//	if err != nil {
	//		return file, err
	//	}
	//}
}

// LoggerWithConfig instance a Logger middleware with config.
//func LoggerWithConfig(conf LoggerConfig) gin.HandlerFunc {
//	formatter := conf.Formatter
//	if formatter == nil {
//		formatter = defaultLogFormatter
//	}
//
//	out := conf.Output
//	if out == nil {
//		out = gin.DefaultWriter
//	}
//
//	notlogged := conf.SkipPaths
//
//	isTerm := true
//
//	if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
//		(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
//		isTerm = false
//	}
//
//	var skip map[string]struct{}
//
//	if length := len(notlogged); length > 0 {
//		skip = make(map[string]struct{}, length)
//
//		for _, path := range notlogged {
//			skip[path] = struct{}{}
//		}
//	}
//
//	return func(c *gin.Context) {
//		// Start timer
//		start := time.Now()
//		path := c.Request.URL.Path
//		raw := c.Request.URL.RawQuery
//		//userAgent := c.Request.UserAgent()
//		//host := c.Request.Host
//
//		// Process request
//		c.Next()
//
//		// Log only when it is not being skipped
//		if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) {
//			return
//		}
//
//		param := LogFormatterParams{
//			Request: c.Request,
//			isTerm:  isTerm,
//			Keys:    c.Keys,
//		}
//
//		// Stop timer
//		param.TimeStamp = time.Now()
//		param.Latency = param.TimeStamp.Sub(start)
//
//		param.ClientIP = c.ClientIP()
//		param.Method = c.Request.Method
//		param.StatusCode = c.Writer.Status()
//		param.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
//
//		param.BodySize = c.Writer.Size()
//
//		if raw != "" {
//			path = path + "?" + raw
//		}
//
//		param.Path = path
//
//		fmt.Fprint(out, formatter(param))
//	}
//}
